﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FCS
{
    #region And

    sealed class And : IFunValue<bool>
    {
        private IFunList<bool> List { get; set; }
        private SingleValCache<bool> _cache = new SingleValCache<bool>();
        private bool _useCache = false;

        public And(IFunList<bool> list)
        {
            List = list;
        }

        public bool Value
        {
            get
            {
                if (!_useCache)
                {
                    foreach (bool val in List)
                    {
                        if (!val)
                        {
                            return false;
                        }
                    }
                    return true;
                }
                else
                {
                    bool ret;
                    if (_cache.TryGetValue(out ret))
                    {
                        return ret;
                    }
                    else
                    {
                        foreach (bool val in List)
                        {
                            if (!val)
                            {
                                _cache.AddVal(false);
                                return false;
                            }
                        }
                        _cache.AddVal(true);
                        return true;
                    }
                }
            }
        }
    }

    #endregion

    #region Or

    sealed class Or : IFunValue<bool>
    {
        private IFunList<bool> List { get; set; }
        private SingleValCache<bool> _cache = new SingleValCache<bool>();
        private bool _useCache = false;

        public Or(IFunList<bool> list)
        {
            List = list;
        }

        public bool Value
        {
            get
            {
                if (!_useCache)
                {
                    int cnt = 0;
                    foreach (bool val in List)
                    {
                        if (val)
                        {
                            return true;
                        }
                        cnt++;
                    }

                    return cnt == 0;
                }
                else
                {
                    bool ret;
                    if (_cache.TryGetValue(out ret))
                    {
                        return ret;
                    }
                    else
                    {
                        int cnt = 0;
                        foreach (bool val in List)
                        {
                            if (val)
                            {
                                _cache.AddVal(true);
                                return true;
                            }
                            cnt++;
                        }

                        _cache.AddVal(cnt == 0);
                        return cnt == 0;
                    }
                }
            }
        }
    }

    #endregion

    #region AndMap

    sealed class AndMap<T> : IFunValue<bool>
    {
        private IFunList<T> List { get; set; }
        private FunPredicate<T> Pred { get; set; }
        private FunPredicateIdx<T> PredIdx { get; set; }
        private SingleValCache<bool> _cache = new SingleValCache<bool>();
        private bool _useCache = false;

        public AndMap(FunPredicate<T> pred, IFunList<T> list)
        {
            List = list;
            Pred = pred;
        }

        public AndMap(FunPredicateIdx<T> predIdx, IFunList<T> list)
        {
            List = list;
            PredIdx = predIdx;
        }

        public bool Value
        {
            get
            {
                if (!_useCache)
                {
                    if (Pred != null)
                    {
                        foreach (T val in List)
                        {
                            if (!Pred(val))
                            {
                                return false;
                            }
                        }
                    }
                    else
                    {
                        int idx = 0;
                        foreach (T val in List)
                        {
                            if (!PredIdx(val, idx))
                            {
                                return false;
                            }
                            idx++;
                        }
                    }
                    return true;
                }
                else
                {
                    bool ret;
                    if (_cache.TryGetValue(out ret))
                    {
                        return ret;
                    }
                    else
                    {
                        if (Pred != null)
                        {
                            foreach (T val in List)
                            {
                                if (!Pred(val))
                                {
                                    _cache.AddVal(false);
                                    return false;
                                }
                            }
                        }
                        else
                        {
                            int idx = 0;
                            foreach (T val in List)
                            {
                                if (!PredIdx(val, idx))
                                {
                                    _cache.AddVal(false);
                                    return false;
                                }
                                idx++;
                            }
                        }
                        _cache.AddVal(true);
                        return true;
                    }
                }
            }
        }
    }

    #endregion

    #region OrMap

    sealed class OrMap<T> : IFunValue<bool>
    {
        private IFunList<T> List { get; set; }
        private FunPredicate<T> Pred { get; set; }
        private FunPredicateIdx<T> PredIdx { get; set; }
        private SingleValCache<bool> _cache = new SingleValCache<bool>();
        private bool _useCache = false;

        public OrMap(FunPredicate<T> pred, IFunList<T> list)
        {
            List = list;
            Pred = pred;
        }

        public OrMap(FunPredicateIdx<T> predIdx, IFunList<T> list)
        {
            List = list;
            PredIdx = predIdx;
        }

        public bool Value
        {
            get
            {
                if (!_useCache)
                {
                    int idx = 0;
                    if (Pred != null)
                    {
                        foreach (T val in List)
                        {
                            if (Pred(val))
                            {
                                return true;
                            }
                            idx++;
                        }
                    }
                    else
                    {
                        foreach (T val in List)
                        {
                            if (PredIdx(val, idx))
                            {
                                return true;
                            }
                            idx++;
                        }
                    }
                    return idx == 0;
                }
                else
                {
                    bool ret;
                    if (_cache.TryGetValue(out ret))
                    {
                        return ret;
                    }
                    else
                    {
                        int idx = 0;
                        if (Pred != null)
                        {
                            foreach (T val in List)
                            {
                                if (Pred(val))
                                {
                                    _cache.AddVal(true);
                                    return true;
                                }
                                idx++;
                            }
                        }
                        else
                        {
                            foreach (T val in List)
                            {
                                if (PredIdx(val, idx))
                                {
                                    _cache.AddVal(true);
                                    return true;
                                }
                                idx++;
                            }
                        }
                        _cache.AddVal(idx == 0);
                        return idx == 0;
                    }
                }
            }
        }
    }

    #endregion

    #region Equals

    sealed class Equals<T> : IFunValue<bool>
    {
        private IFunList<T> List1 { get; set; }
        private IFunList<T> List2 { get; set; }

        public Equals(IFunList<T> list1, IFunList<T> list2)
        {
            List1 = list1;
            List2 = list2;
        }

        public bool Value
        {
            get
            {
                var en1 = List1.GetEnumerator();
                var en2 = List2.GetEnumerator();
                while (true)
                {
                    bool next1 = en1.MoveNext();
                    bool next2 = en2.MoveNext();
                    if (next1 != next2 || (next1 == true && !en1.Current.Equals(en2.Current)))
                    {
                        return false;
                    }
                    if (next1 == false && next2 == false)
                        return true;
                }
            }
        }
    }

    #endregion

    public partial class Functional
    {
        #region And

        public static bool And(IFunList<bool> list)
        {
            return new And(list).Value;
        }
        public static bool And<T>(FunPredicate<T> pred, IFunList<T> list)
        {
            return new AndMap<T>(pred, list).Value;
        }
        public static bool And<T>(FunPredicateIdx<T> pred, IFunList<T> list)
        {
            return new AndMap<T>(pred, list).Value;
        }

        #endregion

        #region Or

        public static bool Or(IFunList<bool> list)
        {
            return new Or(list).Value;
        }
        public static bool Or<T>(FunPredicate<T> pred, IFunList<T> list)
        {
            return new OrMap<T>(pred, list).Value;
        }
        public static bool Or<T>(FunPredicateIdx<T> pred, IFunList<T> list)
        {
            return new OrMap<T>(pred, list).Value;
        }

        #endregion

        #region Equals<T>

		public static bool Equals<T>(IFunList<T> list1, IFunList<T> list2)
        {
            return new Equals<T>(list1, list2).Value;
        }

	    #endregion    
    }
}
